package com.csw.android.androidtest.module.serialize;

import android.view.View;

import com.csw.android.androidtest.ui.LogViewFragment;
import com.csw.android.dev_utils.utils.LogUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.LinkedList;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

/**
 * java提供的对象序列化
 */
public class SerializableTest extends LogViewFragment {

    @Override
    public void initData() {
        super.initData();
        Observable.create(new ObservableOnSubscribe<TestData>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<TestData> emitter) throws Exception {
                TestData testData = new TestData("二", 2, 2.0, logView);
                LinkedList<Integer> serializableData = new LinkedList<>();
                //可序列化对象可以通过ObjectOutputStream和ObjectInputStream进行序列化和反序列化
                //很明显通过流可以把对象转成整型数组，然后存入文件，也可以从文件中读取出来再反序列化成对象
                //写入过程会把每个类的名称等信息也序列化，反序列化时通过反射生成实例赋值，所以可以强转。
                ObjectOutputStream oos = null;
                ObjectInputStream ois = null;
                try {
                    oos = new ObjectOutputStream(new OutputStream() {
                        @Override
                        public void write(int b) throws IOException {
                            serializableData.offerLast(b);
                        }
                    });
                    oos.writeObject(testData);
                    oos.flush();
                    LogUtils.INSTANCE.d("SerializableTest", serializableData.size() * 4 + "");
                    ois = new ObjectInputStream(new InputStream() {
                        @Override
                        public int read() throws IOException {
                            return serializableData.pollFirst();
                        }
                    });
                    //反序列化生成新对象
                    TestData deserializableObject = (TestData) ois.readObject();
                    if (!emitter.isDisposed()) {
                        emitter.onNext(deserializableObject);
                    }
                    if (!emitter.isDisposed()) {
                        emitter.onComplete();
                    }
                } catch (Exception e) {
                    if (!emitter.isDisposed()) {
                        emitter.onError(e);
                    }
                } finally {
                    if (oos != null) {
                        oos.close();
                    }
                    if (ois != null) {
                        ois.close();
                    }
                }
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Consumer<TestData>() {
                    @Override
                    public void accept(TestData testData) throws Exception {
                        logView.addLog(testData.v1);
                        logView.addLog(testData.v2);
                        logView.addLog(testData.v3);
                    }
                })
                .subscribe();
    }

    /**
     * 实现Serializable，将该类标记为可序列化，一旦某个类标记为可序列化，其属性必须都是可序列化的，除非用注解排除，
     * 包括他子类的属性也都需要可序列化。
     */
    public static class TestData implements Serializable {

        //序列化版本id,如果该实例存储到本地文件，后续版本修改了实例，理论上就不能将该实例数据进行反序列化生成
        //新版本的实例对象，所以如果修改了可序列化类导致无法前后兼容，可通过重新生成版本id让其不使用旧版本数据
        //反序列化
        private static final long serialVersionUID = 6949111016147466766L;

        private final String v1;
        private final int v2;
        private final double v3;

        //transient关键字标记该属性不参与序列化反序列化
        transient private final View logView;

        public TestData(String v1, int v2, double v3, View logView) {
            this.v1 = v1;
            this.v2 = v2;
            this.v3 = v3;
            this.logView = logView;
        }

        public String getV1() {
            return v1;
        }

        public int getV2() {
            return v2;
        }

        public double getV3() {
            return v3;
        }
    }
}
